"""
Copyright (C) 2019 Interactive Brokers LLC. All rights reserved. This code is subject to the terms
 and conditions of the IB API Non-Commercial License or the IB API Commercial License, as applicable.
"""


from ibapi.common import UNSET_INTEGER, UNSET_DOUBLE, UNSET_DECIMAL
from ibapi.object_implem import Object
from ibapi.softdollartier import SoftDollarTier

# enum Origin
(CUSTOMER, FIRM, UNKNOWN) = range(3)

# enum AuctionStrategy
(AUCTION_UNSET, AUCTION_MATCH,
 AUCTION_IMPROVEMENT, AUCTION_TRANSPARENT) = range(4)


class OrderComboLeg(Object):
    def __init__(self):
        self.price = UNSET_DOUBLE  # type: float

    def __str__(self):
        return "%f" % self.price


class Order(Object):
    def __init__(self):
        self.softDollarTier = SoftDollarTier("", "", "")
        # order identifier
        self.orderId  = 0
        self.clientId = 0
        self.permId   = 0

        # main order fields
        # order具有的主要的字段，包括action、totalQuantity、orderType、lmtPrice、auxPrice
        # action是订单的方向，字符串，具有两个值，"BUY"(开多或者平空)和"SELL"(开空或者平多)
        self.action = ""
        # totalQuantity代表order交易的量，小数
        self.totalQuantity = UNSET_DECIMAL
        # orderType代表着order的类型，字符串，比如限价单"LMT",市价单"MKT",特别重点,这个参数的值详细注释下
        # 市价单部分包括下面的种类
        # - MKT  市价单,以当前价格成交
        # - MOC 收盘价市价单,尽可能以收盘价执行的订单
        # - MIT 达到市价成交单(Market if Touched),达到指定价格(或者更优于的价格)的时候下市价成交,使用aucPirce设定指定价格
        # - MKT PRT 有保护的市价单(Market with Protection),如果市价单没有立即成交,会立即把整个订单撤掉,下一个限价单,
        #              限价单价格是当卖出的时候比卖出价高一点,买入的时候比买入价低一点,以上是TWS API上的说法.有其他说法是不成交就取消订单
        #              而不是下限价单,需要检验下这两个说法哪个正确.
        # - MTL 市价单不成交转换成限价单(Market to Limit),如果市价单不能完全成交的话,没有成交的部分,以市价单的价格转换成限价单
        # - MKT tif = "OPG",以开盘价成交的订单,下一个市价单,但是成交时间是在下个bar的开盘,可能需要额外设置outsideRth=True
        # - BOX TOP 和MTL很相似,仅仅用于BOX订单中
        # 限价单部分包含的种类
        # - LMT 限价单(Limit),以lmtPrice或者更优的价格成交
        # - LOC 收盘限价单(Limit on Close),收盘的时候,如果收盘价等于或者优于限价单,将会成交
        # - LIT 达到限价成交单(Limit if Touched) 达到指定价格,下限价单,指定价格用auxPrice,限价用lmtPrice
        # - LMT tif = "OPG",在下个bar开盘的时候下一个限价单,限价用lmtPrice
        # 止损单部分包含的种类
        # - STP 止损单(Stop),止损价达到之后下一个市价单成交.使用auxPrice设定止损价
        # - STP LMT 限价止损单(Stop Limit),止损价达到之后,下一个限价单等待成交.使用auxPrice设定止损价,使用lmtPrice设定限价单的价格
        # - STP PRT 有保护的止损单(Stop with Protection),如果达到止损价之后,一下子成交了,和普通的止损单一样,
        #            如果部分成交,会把没有成交的部分撤掉,下一个限价单
        # 移动止损单包含的种类
        # 官网关于止损单的解释:https://www.interactivebrokers.com/en/index.php?f=605
        # - TRAIL 移动止损单(Trailing Stop),当达到止损价格的时候,下一个市价单.止损价格会根据trailStopPrice或者trailingPercent计算得到
        # - TRAIL LIMIT 移动止损限价单(Trailing Stop Limit),当市场价达到止损价的时候,下一个限价单用于止损.在移动止损单的基础之上,
        #               需要额外设置一个lmtPrice,用于设定下限价单的价格
        # - TRAIL LIT 移动止损达到指定价格下限价成交单(Trailing Stop Limit if Touched),
        #               当市场价达到止损价之后,下一个达到指定价格下限价单等待成交.在目前版本的API中，没有找到例子
        # - TRAIL MIT 移动止损达到指定价格下市价成交单(Trailing Stop Market if Touched),当市场价达到止损价之后,
        #               下一个达到指定价格下市价单。在目前版本的API中，没有找到例子
        # 盯住类订单包含的种类
        # - PEG MKT 盯住市场(Pegged To Market) 随着市场上最好的卖价或者买价的价格而不断变动的订单价格
        # - PEG MID 盯住中间价(Pegged To Midpoint) 随着市场上最好的卖价或者买价的平均价而不断变动的订单价格
        # - PEG BENCH 盯住基准(Pegged To Benchmark) 随着市场上另一交易品种价格而不断变动的订单价格
        # - PEG STK 盯住股票(Pegged To Stock) 随着市场上股票价格而不断变动的订单价格,仅用于box
        # - REL 相对订单(Pegged To Primary)  比NBBO更激进的订单价格
        # - PASSV REL 保守的相对订单(Passive Relative) 比NBBO更保守的订单价格
        
        
        self.orderType = ""
        # lmtPrice代表着限定的价格，在限价单等订单类型中使用
        self.lmtPrice      = UNSET_DOUBLE
        # 在很多的订单类型中使用的扩展的价格
        self.auxPrice      = UNSET_DOUBLE

        # extended order fields
        # 一些扩展的订单类型
        # tif代表着订单的有效期，比如 DAY,GTC等，这个字段挺重要的，终点分析一下
        # - DAY 代表着订单在当日有效
        # - DTC 代表着订单经过一天会被沉睡，但是没有取消
        # - IOC 代表着撤销订单中没有立即成交的那部分
        # - FOK 代表着如果订单没有立即完全成交，就撤销整个订单
        # - GTC 代表着订单在撤销前都是有效的
        # - GTD 代表着订单的有效期是被goodTillDate字段决定的
        # - OPG 代表着订单会在开盘的时候被执行,可以在交易时间之外下单,需要设置outsideRth为True
        # - AUC 代表着集合竞价订单
        self.tif = ""                # "Time in Force" - DAY, GTC, etc.
        # 当tif设置为GTC的时候,activeStartTime 代表着 订单开始的时间
        self.activeStartTime = ""   # for GTC orders
        # 当tif设置为GTC的时候,activeStopTime 代表着 订单结束的时间
        self.activeStopTime = ""    # for GTC orders
        # 用于设置 OCA订单组的名称,订单中有这个名称的订单就会被当成一个,如果一个成交了,将会导致其他的取消
        self.ocaGroup = ""          # one cancels all group name
        # 用于设置当OCA订单组中,一个订单部分成交的时候的行为.1代表着其他的会被取消;2代表着其他的会被减少量;
        #                      3代表着其他的会被减少量,不同订单的执行是同时的,速度会快一些,但是可能导致成交几个订单
        self.ocaType        = 0     # 1 = CANCEL_WITH_BLOCK, 2 = REDUCE_WITH_BLOCK, 3 = REDUCE_NON_BLOCK
        self.orderRef       = ""
        # TWS是否把订单发送到服务器中
        self.transmit       = True  # if false, order will be created but not transmited
        # 父订单号，用于附加的订单跟踪究竟附加到哪个订单上使用
        self.parentId       = 0     # Parent order Id, to associate Auto STP or TRAIL orders with the original order.
        # 大宗交易的订单，设置成True的时候，代表着这个订单是大宗交易，需要使用特定的算法用于减少市场冲击
        self.blockOrder     = False
        #  Sweep to Fill订单专用，是否立即以最优的价格执行成交.(据说仅仅用于交易所指定为SMART的时候)
        self.sweepToFill    = False
        self.displaySize    = 0
        self.triggerMethod  = 0     # 0=Default, 1=Double_Bid_Ask, 2=Last, 3=Double_Last, 4=Bid_Ask, 7=Last_or_Bid_Ask, 8=Mid-point
        # outsideRth 代表着 是否可以在交易时间之外下单
        self.outsideRth     = False
        self.hidden         = False
        
        # 代表着在这个时间之后订单才会被激活
        self.goodAfterTime       = ""   # Format: 20060505 08:00:00 {time zone}
        # goodTillDate 代表着 在特定时间点之前订单是有效的
        self.goodTillDate        = ""   # Format: 20060505 08:00:00 {time zone}
        self.rule80A             = ""   # Individual = 'I', Agency = 'A', AgentOtherMember = 'W', IndividualPTIA = 'J', AgencyPTIA = 'U', AgentOtherMemberPTIA = 'M', IndividualPT = 'K', AgencyPT = 'Y', AgentOtherMemberPT = 'N'
        # 代表着 全部成交或者取消
        self.allOrNone      = False
        self.minQty         = UNSET_INTEGER  # type: int
        self.percentOffset  = UNSET_DOUBLE  # type: float  # REL orders only
        self.overridePercentageConstraints = False
        # trailStopPrice  代表着移动止损订单中的止损距离
        self.trailStopPrice = UNSET_DOUBLE  # type: float
        # trailingPercent 代表着移动止损订单中的止损比例
        self.trailingPercent = UNSET_DOUBLE # type: float  # TRAILLIMIT orders only

        # financial advisors only
        self.faGroup              = ""
        self.faProfile            = ""
        self.faMethod             = ""
        self.faPercentage         = ""

        # institutional (ie non-cleared) only
        self.designatedLocation = "" #used only when shortSaleSlot=2
        self.openClose     = ""     # O=Open, C=Close
        self.origin        = CUSTOMER  # 0=Customer, 1=Firm
        self.shortSaleSlot = 0         # type: int  # 1 if you hold the shares, 2 if they will be delivered from elsewhere.  Only for Action=SSHORT
        self.exemptCode    = -1

        # SMART routing only
        # 用于给限价单增加一定的范围，增加限价单可以成交的范围，仅用于交易所设定为SMART的时候
        self.discretionaryAmt = 0
        self.optOutSmartRouting = False

        # BOX exchange orders only
        # 用于auction limit订单中，用于设定auction的策略
        self.auctionStrategy = AUCTION_UNSET # type: int  # AUCTION_MATCH, AUCTION_IMPROVEMENT, AUCTION_TRANSPARENT
        # startingPrice 代表 初始的订单价格
        self.startingPrice   = UNSET_DOUBLE   # type: float
        # stockRefPrice 代表着 如果基准是股票,需要达到这个价格才会开始监控
        self.stockRefPrice   = UNSET_DOUBLE   # type: float
        # delta值，用于Pegged to Stock中确定订单的价格
        self.delta           = UNSET_DOUBLE   # type: float

        # pegged to stock and VOL orders only
        # stockRangeLower 代表着 如果基准是股票,监控的最低价格,低于这个价格将不会监控
        self.stockRangeLower = UNSET_DOUBLE   # type: float
        # stockRangeUpper 代表着 如果基准是股票,监控的最高价格,高于这个价格将不会监控
        self.stockRangeUpper = UNSET_DOUBLE   # type: float

        self.randomizePrice = False
        self.randomizeSize = False

        # VOLATILITY ORDERS ONLY
        # volatility  代表着计算波动率订单价格使用的波动率的百分比
        self.volatility            = UNSET_DOUBLE  # type: float
        # volatilityType 代表着 波动率订单使用的波动率的类型
        self.volatilityType        = UNSET_INTEGER  # type: int  # 1=daily, 2=annual
        self.deltaNeutralOrderType = ""
        self.deltaNeutralAuxPrice  = UNSET_DOUBLE  # type: float
        self.deltaNeutralConId     = 0
        self.deltaNeutralSettlingFirm = ""
        self.deltaNeutralClearingAccount = ""
        self.deltaNeutralClearingIntent = ""
        self.deltaNeutralOpenClose = ""
        self.deltaNeutralShortSale = False
        self.deltaNeutralShortSaleSlot = 0
        self.deltaNeutralDesignatedLocation = ""
        self.continuousUpdate      = False
        self.referencePriceType    = UNSET_INTEGER  # type: int  # 1=Average, 2 = BidOrAsk

        # COMBO ORDERS ONLY
        self.basisPoints     = UNSET_DOUBLE  # type: float  # EFP orders only
        self.basisPointsType = UNSET_INTEGER  # type: int  # EFP orders only

        # SCALE ORDERS ONLY
        self.scaleInitLevelSize  = UNSET_INTEGER  # type: int
        self.scaleSubsLevelSize  = UNSET_INTEGER  # type: int
        self.scalePriceIncrement = UNSET_DOUBLE  # type: float
        self.scalePriceAdjustValue = UNSET_DOUBLE  # type: float
        self.scalePriceAdjustInterval = UNSET_INTEGER  # type: int
        self.scaleProfitOffset = UNSET_DOUBLE  # type: float
        self.scaleAutoReset = False
        self.scaleInitPosition = UNSET_INTEGER   # type: int
        self.scaleInitFillQty = UNSET_INTEGER    # type: int
        self.scaleRandomPercent = False
        self.scaleTable = ""

        # HEDGE ORDERS
        # 对冲订单的类型
        self.hedgeType             = "" # 'D' - delta, 'B' - beta, 'F' - FX, 'P' - pair
        self.hedgeParam            = "" # 'beta=X' value for beta hedge, 'ratio=Y' for pair hedge

        # Clearing info
        self.account               = "" # IB account
        self.settlingFirm          = ""
        self.clearingAccount       = ""   #True beneficiary of the order
        self.clearingIntent        = "" # "" (Default), "IB", "Away", "PTA" (PostTrade)

        # ALGO ORDERS ONLY
        self.algoStrategy          = ""

        self.algoParams            = None    #TagValueList
        # 组合订单的时候设定的订单的方法 ，可以参考TWS API中的例子
        self.smartComboRoutingParams = None  #TagValueList

        self.algoId = ""

        # What-if
        # TWS是否用提供交易的佣金或者保证金的信息代替立即发送订单到服务器
        self.whatIf = False

        # Not Held
        # TWS 是否在IB的订单薄中hold订单,如果是True,将会放到IB order book中,将不会被执行
        self.notHeld = False
        self.solicited = False

        # models
        self.modelCode = ""

        # order combo legs
        # 组合订单的leg设置，用于 Combo Limit with Price per Leg订单
        self.orderComboLegs = None  # OrderComboLegListSPtr

        self.orderMiscOptions = None  # TagValueList

        # VER PEG2BENCH fields:
        # peg to benchmark 订单设定的变量
        # referenceContractId 代表着 peg to benchmark order盯住的基准合约
        self.referenceContractId = 0
        # peggedChangeAmount 代表着 peg to benchmark order中当基准合约的价格变化大于了referenceChangeAmount的时候调整的order price的量
        self.peggedChangeAmount = 0.
        # isPeggedChangeAmount 代表着 peg to benchmark order中盯住的订单价格的变化是导致order price价格的增加还是减少
        self.isPeggedChangeAmountDecrease = False
        # referenceChangeAmount 代表着 peg to benchmark order盯住的基准合约需要调整的价格
        self.referenceChangeAmount = 0.
        # referenceExchangeId 代表着 peg to benchmark order盯住的基准合约所在的交易所
        self.referenceExchangeId = ""
        # 调整订单的类型，比如，设置成STP，当条件被触发之后，将会调整订单为STP
        self.adjustedOrderType = ""
        
        # 当触发价格达到的时候
        self.triggerPrice = UNSET_DOUBLE
        # 调整为STP订单的时候，止损价设置的价位
        self.adjustedStopPrice = UNSET_DOUBLE
        # 当转换成的止损订单类型为"STP LMT"的时候，止损被触发之后，下的限价单的价格
        self.adjustedStopLimitPrice = UNSET_DOUBLE
        # 当调整为"TRAIL"订单之后，移动止损的量
        self.adjustedTrailingAmount = UNSET_DOUBLE
        # 当调整为"TRAIL"订单之后，移动止损的amount或者percent，TWS API这样解释，有点绕
        self.adjustableTrailingUnit = 0
        self.lmtPriceOffset = UNSET_DOUBLE

        self.conditions = []  # std::vector<std::shared_ptr<OrderCondition>>
        self.conditionsCancelOrder = False
        self.conditionsIgnoreRth = False

        # ext operator
        self.extOperator = ""

        # native cash quantity
        # Forex Cash Quantity Order订单使用
        self.cashQty = UNSET_DOUBLE

        self.mifid2DecisionMaker = ""
        self.mifid2DecisionAlgo = ""
        self.mifid2ExecutionTrader = ""
        self.mifid2ExecutionAlgo = ""

        self.dontUseAutoPriceForHedge = False

        self.isOmsContainer = False

        self.discretionaryUpToLimitPrice = False

        self.autoCancelDate = ""
        self.filledQuantity = UNSET_DECIMAL
        self.refFuturesConId = 0
        self.autoCancelParent = False
        self.shareholder = ""
        self.imbalanceOnly = False
        self.routeMarketableToBbo = False
        self.parentPermId = 0

        self.usePriceMgmtAlgo = None
        self.duration = UNSET_INTEGER
        self.postToAts = UNSET_INTEGER

    def __str__(self):
        s = "%s,%d,%s:" % (self.orderId, self.clientId, self.permId)

        s += " %s %s %d@%f" % (
            self.orderType,
            self.action,
            self.totalQuantity,
            self.lmtPrice)

        s += " %s" % self.tif

        if self.orderComboLegs:
            s += " CMB("
            for leg in self.orderComboLegs:
                s += str(leg) + ","
            s += ")"

        if self.conditions:
            s += " COND("
            for cond in self.conditions:
                s += str(cond) + ","
            s += ")"

        return s
